﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Benchmark.Db.Base;
using System.Data.SqlClient;
using Benchmark.Db.Adapter.MsSql.Properties;
using System.Data.Linq.Mapping;
using System.Reflection;
using Benchmark.Db.Base.DataObjects;
using System.Data.Linq;

namespace Benchmark.Db.Adapter.MsSql
{
    public class LinqToSqlConnector : SqlConnectorBase, IDbBenchmarkConnectionV1
    //where T : class, IDataObject, new()
    {
        public string Name { get { return "Linq to Sql Server Connector"; } }
        public string Version { get { return "1.0"; } }
        
        DataContext m_dataContext;

        public LinqToSqlConnector()
        {

        }

        public void Initialize<T>()
            where T : class, IDataObject, new()
        {

            this.LoadConnectionString();
            this.Connection = new SqlConnection(this.ConnectionString);
            this.Connection.Open();

            m_dataContext = new LinqToSqlDataContext<T>(this.Connection);

            if (!this.m_dataContext.DatabaseExists())
                this.m_dataContext.CreateDatabase();

            this.EnsureTable<T>();
        }

        public void ClearDB()
        {
            this.m_dataContext.DeleteDatabase();
            this.m_dataContext.CreateDatabase();
        }

        public void Insert<T>(T[] records)
            where T : class, IDataObject, new()
        {
            LinqToSqlDataContext<T> context = (LinqToSqlDataContext<T>)this.m_dataContext;
            for (int i = 0; i < records.Length; i++)
            {
                context.Entities.InsertOnSubmit(records[i]);
                this.m_dataContext.SubmitChanges();
            }
        }

        public void BulkInsert<T>(T[] records)
            where T : class, IDataObject, new()
        {
            LinqToSqlDataContext<T> context = (LinqToSqlDataContext<T>)this.m_dataContext;
            context.Entities.InsertAllOnSubmit(records);
            this.m_dataContext.SubmitChanges();
        }

        private string GetInParameter<K>(K[] keys)
        {
            Type paramType = typeof(K);

            bool useQuotes = paramType == typeof(string) || paramType == typeof(DateTime);

            StringBuilder builder = new StringBuilder();

            for (int i = 0; i < keys.Length; i++)
            {
                if (useQuotes)
                    builder.AppendFormat("'{0}'", keys[i]);
                else
                    builder.AppendFormat("{0}", keys[i]);

                if (i < keys.Length - 1)
                    builder.Append(",");
            }

            return builder.ToString();
        }

        public void Load<T, K>(string keyColumnName, K[] keys)
            where T : class, IDataObject, new()
        {
            //http://www.west-wind.com/weblog/posts/2007/Aug/28/Dynamic-Expressions-in-Linq-to-Sql
            MetaTable metaTable = this.m_dataContext.Mapping.GetTable(typeof(T));
            string tableName = metaTable.TableName;

            string parameters = this.GetInParameter(keys);
            string Sql = string.Format("select * from [{0}] where [{1}] in ({2})", tableName, keyColumnName, parameters);

            IEnumerable<T> entityList = this.m_dataContext.ExecuteQuery<T>(Sql, null);
        }

        public void Update<T>(string keyColumnName, T[] records)
            where T : class, IDataObject, new()
        {
            LinqToSqlDataContext<T> context = (LinqToSqlDataContext<T>)this.m_dataContext;
            for (int i = 0; i < records.Length; i++)
            {
                context.Entities.Attach(records[i], true);
                context.SubmitChanges();
            }
        }

        public void BulkUpdate<T>(string keyColumnName, T[] records)
            where T : class, IDataObject, new()
        {
            LinqToSqlDataContext<T> context = (LinqToSqlDataContext<T>)this.m_dataContext;
            for (int i = 0; i < records.Length; i++)
                context.Entities.Attach(records[i], true);

            context.SubmitChanges();
        }

        public void Delete<T, K>(K[] keys)
            where T : class, IDataObject, new()
        {
            LinqToSqlDataContext<T> context = (LinqToSqlDataContext<T>)this.m_dataContext;
            throw new NotSupportedException();
        }

        public void BeginTransaction()
        {
            this.m_dataContext.Transaction = this.Connection.BeginTransaction();
        }

        public void CommitTransaction()
        {
            this.m_dataContext.Transaction.Commit();
        }

        public void RollbackTransaction()
        {
            this.m_dataContext.Transaction.Rollback();
        }

        public override string ToString()
        {
            return this.Name;
        }
    }
}
